﻿using Force.Crc32;
using System.IO.MemoryMappedFiles;

namespace DHTools
{
    public class SharedMemoryReader : IDisposable
    {
        private const string MapName = "SharedMemoryCustom";
        private const string SemaphoreName = "SharedMemorySemaphore";
        private const string MutexName = "SharedMemoryMutex";
        private const int MemorySize = 1024 * 1024 * 10; // 10MB

        private readonly MemoryMappedFile _mmf;
        private readonly Semaphore _semaphore;
        private readonly Mutex _mutex;

        public delegate void Log(string message);
        public event Log LogError;

        public SharedMemoryReader()
        {
            try
            {
                _mmf = MemoryMappedFile.OpenExisting(MapName);
                _semaphore = Semaphore.OpenExisting(SemaphoreName);
                _mutex = Mutex.OpenExisting(MutexName);
            }
            catch (Exception ex)
            {
                LogError?.Invoke("Failed to initialize shared memory tools: " + ex.Message);
                throw;
            }
        }

        /// <summary>
        /// Reads data from shared memory.
        /// </summary>
        public (DateTime Timestamp, DataType DataType, byte[] Data) ReadData()
        {
            _semaphore.WaitOne();
            _mutex.WaitOne();

            try
            {
                using (var accessor = _mmf.CreateViewAccessor())
                {
                    int offset = 0;

                    ulong time = accessor.ReadUInt64(offset); offset += 8;
                    int dataTypeValue = accessor.ReadInt32(offset); offset += 4;
                    ulong dataLength = accessor.ReadUInt64(offset); offset += 8;
                    uint chunkIndex = accessor.ReadUInt32(offset); offset += 4;
                    byte compressFlag = accessor.ReadByte(offset); offset += 1;
                    byte[] reserved = new byte[3];
                    accessor.ReadArray(offset, reserved, 0, 3); offset += 3;

                    byte[] data = new byte[dataLength];
                    accessor.ReadArray(offset, data, 0, (int)dataLength); offset += (int)dataLength;

                    uint checksum = accessor.ReadUInt32(offset);

                    if (checksum != Crc32Algorithm.Compute(data))
                    {
                        throw new Exception("Data checksum mismatch!");
                    }

                    DateTime timestamp = DateTimeOffset.FromUnixTimeSeconds((long)time).DateTime;
                    DataType dataType = (DataType)dataTypeValue;
                    return (timestamp, dataType, data);
                }
            }
            finally
            {
                _mutex.ReleaseMutex();
            }
        }

        public void Dispose()
        {
            _mmf?.Dispose();
            _semaphore?.Dispose();
            _mutex?.Dispose();
        }
    }
}